#include <iostream>
#include <thread>
#include <mutex>
#include <ctime>
#include <array>
#include <cstdlib>
#include <stdio.h>
#include <time.h>
#include <chrono>

// modify the number of philosophers
#define PHILOSOPHERS 10

using namespace std;
using namespace std::this_thread;
using namespace std::chrono;

mutex chopstick[PHILOSOPHERS];
mutex output;

int meal_time[PHILOSOPHERS];

// initialize the meal time
void init()
{
	for (int i = 0; i < PHILOSOPHERS; ++i)
		meal_time[i] = 50;
}

void eat(int id)
{
	int id1, id2;
	if ((id & 1) == 1)
	{
		id1 = id;
		id2 = (id + 1) % PHILOSOPHERS;
	}
	else
	{
		id1 = (id + 1) % PHILOSOPHERS;
		id2 = id;
	}

	while (true)
	{
		chopstick[id1].lock();
		chopstick[id2].lock();
		output.lock();
		cout << "Philosopher " << id + 1 << " begin eating ..." << endl;
		output.unlock();

		int eated_time = rand() % (30 + 1);

		// eating ...
		sleep_for(seconds(eated_time % 10));

		meal_time[id] -= eated_time;

		// finished eating
		if (meal_time[id] <= 0)
		{
			output.lock();
			cout << "Philosopher " << id + 1 << " finished." << endl;
			output.unlock();
			chopstick[id1].unlock();
			chopstick[id2].unlock();
			break;
		}
		output.lock();
		cout << "Philosopher " << id + 1 << " still need " << meal_time[id] << " minutes to eat." << endl;
		output.unlock();
		chopstick[id1].unlock();
		chopstick[id2].unlock();
		// thinking ...
		sleep_for(milliseconds(200));
	}
}

int main()
{
	//initialize each Philosopher's meal time.
	init();

	array<thread, PHILOSOPHERS> threads;

	cout << "Starting..." << endl;
	for (int i = 0; i < PHILOSOPHERS; ++i)
		threads[i] = thread(eat, i);

	for (int i = 0; i < PHILOSOPHERS; ++i)
		threads[i].join();

	printf("All philosophers done.\n");
	return 0;
}